package com.pig4cloud.pig.gateway.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.ttl.TtlRunnable;
import com.pig4cloud.pig.gateway.config.GatewayConfiguration;
import com.bblocks.common.Constant;
import com.bblocks.enums.IdentityEnum;
import com.bblocks.enums.MDCFieldEnum;
import com.bblocks.util.TraceIdUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author zhangran
 * @date 2021/7/13
 * <p>
 * 全局拦截器，作用所有的微服务
 * <p>
 * 1. 对请求的API调用过滤，记录接口的请求时间，方便日志审计、告警、分析等运维操作 2. 后期可以扩展对接其他日志系统
 * <p>
 */
@Slf4j
//@Component
//@Order(1)
public class ApiLoggingFilter implements GlobalFilter, Ordered {

	private static final String START_TIME = "startTime";

	private static final String X_REAL_IP = "X-Real-IP";// nginx需要配置,失敗的情況下 使用headers
	private static final String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		if(exchange.getAttributes().get(START_TIME) != null)
			return chain.filter(exchange);

		exchange.getAttributes().put(START_TIME, System.nanoTime());
		//设置请求ID（根据请求头 Constant.REQ_SEQUENCE_KEY）
		List<String> reqSequence = exchange.getRequest().getHeaders().get(Constant.REQ_SEQUENCE_KEY);
		if (CollectionUtil.isEmpty(reqSequence)) {
			TraceIdUtils.setTraceId(IdentityEnum.Gateway.name(), exchange.getRequest().getURI().getPath(), null);
		} else
			TraceIdUtils.setTraceId(IdentityEnum.OutSide.name(), exchange.getRequest().getURI().getPath(), reqSequence.get(0));

		String url = exchange.getRequest().getURI().getPath();
		String ip = getClientIp(exchange.getRequest());
		String api = exchange.getRequest().getURI().getRawPath();
		String token = exchange.getAttribute(Constant.ACCESS_TOKEN_KEY);

		MDC.put(MDCFieldEnum.IP.name(), ip);
		MDC.put(MDCFieldEnum.URL.name(), url);
		MDC.put(MDCFieldEnum.TOKEN.name(), token);
		if (log.isInfoEnabled()) {

			/**
			 String info = String.format("Method:{%s} Host:{%s} Path:{%s} Query:{%s}",
			 exchange.getRequest().getMethod().name(), exchange.getRequest().getURI().getHost(),
			 exchange.getRequest().getURI().getPath(), exchange.getRequest().getQueryParams());
			 **/

			log.info("Method:{} Host:{} Path:{} Query:{}"
					, exchange.getRequest().getMethod().name(), exchange.getRequest().getURI().getHost(),
					url, exchange.getRequest().getQueryParams()
					/**,exchange.getRequest().getHeaders() **/
			);
		}

		exchange.getAttributes().put(GatewayConfiguration.CONTEXT_MAP, MDC.getCopyOfContextMap());

			//Map<String, String> contextMap = MDC.getCopyOfContextMap();
			return chain.filter(exchange).then(Mono.fromRunnable(() -> {
				//log.info("响应的url:{},結果!", url);

				MDC.setContextMap((Map<String, String>) exchange.getAttributes().get(GatewayConfiguration.CONTEXT_MAP));
				Long startTime = exchange.getAttribute(START_TIME);
				if (startTime != null) {
					Long executeTime = (System.nanoTime() - startTime);
					MDC.put(MDCFieldEnum.COST.name(), String.valueOf(executeTime));
					int code = exchange.getResponse().getStatusCode() != null
							? exchange.getResponse().getStatusCode().value() : 500;
					// 当前仅记录日志，后续可以添加日志队列，来过滤请求慢的接口
					if (log.isInfoEnabled()) {
						log.info("来自IP地址：{} 的请求：{}，响应状态码：{}，请求耗时：{}ms,{}", ip, url, code, String.format("%.2f", (double)executeTime/1000000), TraceIdUtils.getTraceId(false));
					}
				}
				//TraceIdUtils.endTraceId();
				MDC.clear();
			}));
	}

	@Override
	public int getOrder() {
		return 1;
	}

	private String getClientIp(ServerHttpRequest request) {
		List<String> ips = request.getHeaders().get(X_REAL_IP);
		String ip = CollectionUtil.isNotEmpty(ips) ? ips.get(0) : null;
		if (StrUtil.isNotEmpty(ip)) {
			return ip;
		} else {
			return getClientIPByHeader(request, headers);
		}
	}

	private String getClientIPByHeader(ServerHttpRequest request, String... headerNames) {
		String ip;
		for (String header : headerNames) {
			List<String> ips = request.getHeaders().get(header);
			ip = CollectionUtil.isNotEmpty(ips) ? ips.get(0) : null;
			if (false == NetUtil.isUnknown(ip)) {
				return NetUtil.getMultistageReverseProxyIp(ip);
			}
		}

		ip = request.getRemoteAddress().getHostString();
		return NetUtil.getMultistageReverseProxyIp(ip);
	}

}
